#include <vector>
#include <hash_map>
#include <map>
#include <stdio.h>
#include <io.h>
#include <iostream>
#include <time.h>
#include "hse_common.h"
#include "hse_types.h"
#include "document_reader.h"
#include "structured_index.h"
#include "index_builder.h"
#include "document_meta_datas.h"

namespace hse {

using stdext::hash_map;
using std::vector;
using std::map;
using std::cerr;
using std::cout;
using std::endl;

IndexBuilder::IndexBuilder(const char *index_dir) 
: index_dir_(index_dir) {

}

void IndexBuilder::AddDocumentsInDir(const char *dir) {
	LOG_INFO(printf("Building index for directory:%s\n", dir));
	DocumentReader::Open();
	int dir_len = strlen(dir);
	int total_file = 0;
	clock_t start_time;
	char *files = new char[strlen(dir) + 6]; //*.txt
	strcpy(files, dir);
	strcat(files, "*.txt");
	_finddata_t file;
	long longf;

	int total_files = 0;
	if((longf = _findfirst(files, &file))==-1l)  { 
		std::cerr << "directory not exist/directory is empty!" << endl; 
	} else {  
		while( _findnext( longf, &file ) == 0 ) 
			if (file.attrib != _A_SUBDIR) total_files++;
	}

	if((longf = _findfirst(files, &file))==-1l)  { 
		std::cerr << "directory not exist/directory is empty!" << endl; 
	} else {  
		start_time = clock();
		while( _findnext( longf, &file ) == 0 ) {
			LOG_DEBUG(cout << "find candiates file:" << file.name << ",attribute" << file.attrib << endl);
			if (file.attrib != _A_SUBDIR) {
				char filename[260];
				strcpy(filename, dir);
				strcat(filename, file.name);
				AddDocument(filename);
				total_file++;
				LOG_INFO(
					if (total_file % 50 == 0)
						printf("finish %d/%d files in %d millseconds!(estimate %d secnods left)\n", total_file, total_files, 
							(clock() - start_time), (clock() - start_time) * (total_files - total_file) / total_file / 1000)
				);
			}
		}
	}
	delete[] files;
	DocumentReader::Close();
	LOG_INFO(printf("use %d millseonds to build index for %d files\n", (clock() - start_time), total_file));
}

void IndexBuilder::AddDocument(const char *doc_name) {
	LOG_DEBUG(printf("--------Build index for file:%s------\n", doc_name));
	DocumentReader reader(doc_name);
	reader.Init();
	vector<DocWord*>& words = reader.GetWords();
	int doc_id = reader.GetId();
	for(size_t i = 0; i < words.size(); ++i) {
		map<string, Posting*, StringLess>::iterator& i_pos = postings.find(words[i]->word);
		if (i_pos == postings.end()) {
			//Create new posting
			Posting *posting = new Posting();
			posting->term = words[i]->word;
			DocPositions* pos = new DocPositions();
			pos->count = 1;
			pos->doc_id = doc_id;
			pos->positions.push_back(words[i]->position);
			posting->doc_positions.push_back(pos);
			postings[words[i]->word] = posting;
			DEBUG_F(printf("Create new posting:term:%s\t count:%d\t doc_id:%d pos:%d\n", posting->term.c_str(), pos->count, pos->doc_id, words[i]->position));
		} else {
			Posting* posting = i_pos->second;
			DocPositions* last = posting->doc_positions.back();
			if (posting->doc_positions.back()->doc_id == doc_id) {
				//append a position to exist doc
				posting->doc_positions.back()->positions.push_back(words[i]->position);
				posting->doc_positions.back()->count++;
				DEBUG_F(printf("Append a position:term:%s\t count:%d\t doc_id:%d\n", posting->term.c_str(), posting->doc_positions.back()->count, posting->doc_positions.back()->doc_id));
			} else {
				//create new doc_poitions for a different doc
				DocPositions* doc_positions = new DocPositions;
				doc_positions->count = 1;
				doc_positions->doc_id = doc_id;
				doc_positions->positions.push_back(words[i]->position);
				posting->doc_positions.push_back(doc_positions);
				DEBUG_F(printf("Append a new position:term:%s\t count:%d\t doc_id:%d\n", posting->term.c_str(), 1, doc_id));
			}
		}
	}

	DocumentMetaData meta;
	meta.doc_id = reader.GetId();
	meta.full_name = string(doc_name);
	meta.length = reader.GetLength();
	meta.name = string(doc_name);
	meta.word_count = reader.GetWords().size();
	meta.type = 't';
	DocumentMetaDatas::Instance().AddMetaData(meta);
}



int IndexBuilder::Dump() {
	string file_index = index_dir_ + string(FILE_POSTING);
	FILE* f = fopen(file_index.c_str(), "wb");
	StructuredIndex index(postings);
	index.Dump(f);
	fclose(f);

	string file_meta = index_dir_ + string(FILE_META);
	f = fopen(file_meta.c_str(), "wb");
	DocumentMetaDatas::Instance().Dump(f);
	fclose(f);
	return 0;
}


IndexBuilder::~IndexBuilder() {
	//TODO:free hash_map Posting, Posting's DocPosition
	map<string, Posting*, StringLess>::iterator i = postings.begin();
	for (; i != postings.end(); i++) {
		vector<DocPositions*>::iterator ps = i->second->doc_positions.begin();
		for (; ps != i->second->doc_positions.end(); ps++) {
			delete (*ps);
		}
		delete i->second;
	}
}


}
